/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.context.request;

import java.security.Principal;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;

import org.springframework.lang.Nullable;

/**
 * Generic interface for a web request. Mainly intended for generic web
 * request interceptors, giving them access to general request metadata,
 * not for actual handling of the request.
 *
 * @author Juergen Hoeller
 * @author Brian Clozel
 * @see WebRequestInterceptor
 * @since 2.0
 */
public interface WebRequest extends RequestAttributes {

    /**
     * Return the request header of the given name, or {@code null} if none.
     * <p>Retrieves the first header value in case of a multi-value header.
     *
     * @see javax.servlet.http.HttpServletRequest#getHeader(String)
     * @since 3.0
     */
    @Nullable
    String getHeader(String headerName);

    /**
     * Return the request header values for the given header name,
     * or {@code null} if none.
     * <p>A single-value header will be exposed as an array with a single element.
     *
     * @see javax.servlet.http.HttpServletRequest#getHeaders(String)
     * @since 3.0
     */
    @Nullable
    String[] getHeaderValues(String headerName);

    /**
     * Return a Iterator over request header names.
     *
     * @see javax.servlet.http.HttpServletRequest#getHeaderNames()
     * @since 3.0
     */
    Iterator<String> getHeaderNames();

    /**
     * Return the request parameter of the given name, or {@code null} if none.
     * <p>Retrieves the first parameter value in case of a multi-value parameter.
     *
     * @see javax.servlet.http.HttpServletRequest#getParameter(String)
     */
    @Nullable
    String getParameter(String paramName);

    /**
     * Return the request parameter values for the given parameter name,
     * or {@code null} if none.
     * <p>A single-value parameter will be exposed as an array with a single element.
     *
     * @see javax.servlet.http.HttpServletRequest#getParameterValues(String)
     */
    @Nullable
    String[] getParameterValues(String paramName);

    /**
     * Return a Iterator over request parameter names.
     *
     * @see javax.servlet.http.HttpServletRequest#getParameterNames()
     * @since 3.0
     */
    Iterator<String> getParameterNames();

    /**
     * Return a immutable Map of the request parameters, with parameter names as map keys
     * and parameter values as map values. The map values will be of type String array.
     * <p>A single-value parameter will be exposed as an array with a single element.
     *
     * @see javax.servlet.http.HttpServletRequest#getParameterMap()
     */
    Map<String, String[]> getParameterMap();

    /**
     * Return the primary Locale for this request.
     *
     * @see javax.servlet.http.HttpServletRequest#getLocale()
     */
    Locale getLocale();

    /**
     * Return the context path for this request
     * (usually the base path that the current web application is mapped to).
     *
     * @see javax.servlet.http.HttpServletRequest#getContextPath()
     */
    String getContextPath();

    /**
     * Return the remote user for this request, if any.
     *
     * @see javax.servlet.http.HttpServletRequest#getRemoteUser()
     */
    @Nullable
    String getRemoteUser();

    /**
     * Return the user principal for this request, if any.
     *
     * @see javax.servlet.http.HttpServletRequest#getUserPrincipal()
     */
    @Nullable
    Principal getUserPrincipal();

    /**
     * Determine whether the user is in the given role for this request.
     *
     * @see javax.servlet.http.HttpServletRequest#isUserInRole(String)
     */
    boolean isUserInRole(String role);

    /**
     * Return whether this request has been sent over a secure transport
     * mechanism (such as SSL).
     *
     * @see javax.servlet.http.HttpServletRequest#isSecure()
     */
    boolean isSecure();

    /**
     * Check whether the requested resource has been modified given the
     * supplied last-modified timestamp (as determined by the application).
     * <p>This will also transparently set the "Last-Modified" response header
     * and HTTP status when applicable.
     * <p>Typical usage:
     * <pre class="code">
     * public String myHandleMethod(WebRequest webRequest, Model model) {
     *   long lastModified = // application-specific calculation
     *   if (request.checkNotModified(lastModified)) {
     *     // shortcut exit - no further processing necessary
     *     return null;
     *   }
     *   // further request processing, actually building content
     *   model.addAttribute(...);
     *   return "myViewName";
     * }</pre>
     * <p>This method works with conditional GET/HEAD requests, but
     * also with conditional POST/PUT/DELETE requests.
     * <p><strong>Note:</strong> you can use either
     * this {@code #checkNotModified(long)} method; or
     * {@link #checkNotModified(String)}. If you want enforce both
     * a strong entity tag and a Last-Modified value,
     * as recommended by the HTTP specification,
     * then you should use {@link #checkNotModified(String, long)}.
     * <p>If the "If-Modified-Since" header is set but cannot be parsed
     * to a date value, this method will ignore the header and proceed
     * with setting the last-modified timestamp on the response.
     *
     * @param lastModifiedTimestamp the last-modified timestamp in
     * milliseconds that the application determined for the underlying
     * resource
     * @return whether the request qualifies as not modified,
     * allowing to abort request processing and relying on the response
     * telling the client that the content has not been modified
     */
    boolean checkNotModified(long lastModifiedTimestamp);

    /**
     * Check whether the requested resource has been modified given the
     * supplied {@code ETag} (entity tag), as determined by the application.
     * <p>This will also transparently set the "ETag" response header
     * and HTTP status when applicable.
     * <p>Typical usage:
     * <pre class="code">
     * public String myHandleMethod(WebRequest webRequest, Model model) {
     *   String eTag = // application-specific calculation
     *   if (request.checkNotModified(eTag)) {
     *     // shortcut exit - no further processing necessary
     *     return null;
     *   }
     *   // further request processing, actually building content
     *   model.addAttribute(...);
     *   return "myViewName";
     * }</pre>
     * <p><strong>Note:</strong> you can use either
     * this {@code #checkNotModified(String)} method; or
     * {@link #checkNotModified(long)}. If you want enforce both
     * a strong entity tag and a Last-Modified value,
     * as recommended by the HTTP specification,
     * then you should use {@link #checkNotModified(String, long)}.
     *
     * @param etag the entity tag that the application determined
     * for the underlying resource. This parameter will be padded
     * with quotes (") if necessary.
     * @return true if the request does not require further processing.
     */
    boolean checkNotModified(String etag);

    /**
     * Check whether the requested resource has been modified given the
     * supplied {@code ETag} (entity tag) and last-modified timestamp,
     * as determined by the application.
     * <p>This will also transparently set the "ETag" and "Last-Modified"
     * response headers, and HTTP status when applicable.
     * <p>Typical usage:
     * <pre class="code">
     * public String myHandleMethod(WebRequest webRequest, Model model) {
     *   String eTag = // application-specific calculation
     *   long lastModified = // application-specific calculation
     *   if (request.checkNotModified(eTag, lastModified)) {
     *     // shortcut exit - no further processing necessary
     *     return null;
     *   }
     *   // further request processing, actually building content
     *   model.addAttribute(...);
     *   return "myViewName";
     * }</pre>
     * <p>This method works with conditional GET/HEAD requests, but
     * also with conditional POST/PUT/DELETE requests.
     * <p><strong>Note:</strong> The HTTP specification recommends
     * setting both ETag and Last-Modified values, but you can also
     * use {@code #checkNotModified(String)} or
     * {@link #checkNotModified(long)}.
     *
     * @param etag the entity tag that the application determined
     * for the underlying resource. This parameter will be padded
     * with quotes (") if necessary.
     * @param lastModifiedTimestamp the last-modified timestamp in
     * milliseconds that the application determined for the underlying
     * resource
     * @return true if the request does not require further processing.
     * @since 4.2
     */
    boolean checkNotModified(@Nullable String etag, long lastModifiedTimestamp);

    /**
     * Get a short description of this request,
     * typically containing request URI and session id.
     *
     * @param includeClientInfo whether to include client-specific
     * information such as session id and user name
     * @return the requested description as String
     */
    String getDescription(boolean includeClientInfo);

}
